let ctx,
    planet,
    water = [],
    trees = [],
    clouds = [];

/*   by zxc 2020,12,29 TO adapt vue  ⬇ */
// ==================   用户自定义的setting，这里是用于动态绑定的 by setSettings ===================
let custom_settings = {
    maxTreesCount: 0, // 树的最大棵树

    // system
    waterLevelColour: 'rgba(40,160,220,0.4)',
}

// ==================   额外的setting，在初始化之后就不能改变的，可修改 by setSettings ===================
let external_settings = {

    // chance
    treeChance: 200, // 树产生的概率
    fruitChance: 400, // 果子产生的概率
    cloudChance: 1500, // 云产生的概率
    rainingChance: 100, // 云朵下雨（雨天）的概率
    cloudDeadChance: 40, // 云朵消失的概率

    // rate
    rainRate: 10,

    // time
    rainTime: 70,// 下雨的时间

}

// ==================   默认的setting，在初始化之后就不能改变的，可修改 by setSystemSettings ===================
let settings = {

    // 0.1 核心恢复的时间  one-loop
    core_recover_time: 500,
    // 0.2 核心恢复速率
    core_recover_rate: 0.5,

    // 1.大气层的高度
    atmosphere_height: 70,
    // 2.大气层透明度的生长速度
    atmosphere_growth: 0.0002,
    // 3.大气层的透明度
    atmosphere_max: 0.1,
    // 4.重力加速度
    gravity: 0.3,
    // 5.水的吸收速率
    soak_rate: 0.05,
    // 6.变冷的速率
    cool_rate: 0.5,
    // 7.变暖的速率
    melt_rate: 0.01,
    // 8.干燥速率
    dry_rate: 0.01,
    // 9.能容纳树的个数
    max_trees: 20,
    // 10.树木的尺寸，单位度。 18度
    tree_spread: 18,
    // 11.地球自转速度
    spin_rate: 0.1,
    // 12.树木的高度生长速率
    tree_growth_rate: 0.1,
    // 12.1 树木的高度衰退速率
    tree_down_rate: 0.01,
    // 13.树木的默认高度
    tree_height: 18,
    // 14.树木的分支的半径宽度 px
    branch_size: 6,
    // 15.树木的分支的半径增长宽度 px
    branch_growth: 0.3,
    // 16.树木的分支上果子的最大数量,这些果子可能会坏掉
    max_fruit: 1,
    // 17.果子的坏掉的时间  one-loop
    fruit_rot: 1000,
    // 18.果子掉落的时间 one-loop
    fruit_age: 400,
    // 19.果子长大的速率 px
    fruit_growth: 0.02,
    // 19.果子大小半径 px
    fruit_size: 2,
    // 20.果子掉落的速度
    fruit_gravity: 0.5,
    // 21.雪层消失出现的速率 px
    freeze_rate: 0.1,
    // 22.雪层的高度 px
    snow_height: 3,
    // 23.树死亡所需要的时间
    drown_time: 100,
    // 24.没有被淹没,土壤有水分，树木慢慢死亡
    tree_slow_growth_rate: 0.01,
    // 25.被淹没树木,且已经成熟
    tree_fast_growth_rate: 0.5,
    // 26.果子（成熟度）透明度变换的速率
    fade_rate: 0.01,
    // 27.水位线高于这个值，云朵将会消失
    cloud_cutoff: 15,


    cold_core_colour: '#A1A1A1',
    core_colour: '#FCCD49',
    wet_soil_colour: '#7A4500',
    tree_colour: '#8F5424',
    branch_colours: ['#83DE28', '#ABCF51', '#9BD604', '#76C967', '#3D942E'],
    branch_dead_colour: '#C29D38',
    fruit_colours: ['#FF0000', '#DE1F6C', '#E87E46', '#DE4112', '#F27F1B'],
    rotten_fruit_colour: 'rgba(131, 179, 0,'
};

export const setCtx = function (your_ctx) {
    ctx = your_ctx;
}

export const setPWTC = function (y_planet, y_water, y_trees, y_clouds) {
    planet = y_planet;
    water = y_water;
    trees = y_trees;
    clouds = y_clouds
}

export const setSettings = function (your_settings) {
    custom_settings = your_settings;
    external_settings = {
        ...external_settings,
        ...your_settings
    };
}

/*   by zxc 2020,12,29 TO adapt vue  ⬆ */

export const Utils = {

    circle_angle: function (x, y, r, a) {
        let xc = Math.cos(a * (Math.PI / 180)) * r + x;
        let yc = Math.sin(a * (Math.PI / 180)) * r + y;
        return {
            x: xc,
            y: yc
        };
    },

    alter_angle: function (a, n) {
        let angle = a + n;
        if (angle < 0) angle = 360 + angle;
        if (angle > 360) angle = angle - 360;
        return angle;
    },

    in_angle: function (angle1, angle2, range) {
        let diff = angle1 - angle2;

        return Math.abs(diff % 360) <= range || (360 - Math.abs(diff % 360)) <= range;
    },

    chance: function (number) {
        return (Math.random() * number < 1);
    },

    distance: function (point1, point2) {
        return (Math.sqrt(Math.pow(point1.x - point2.x, 2) + Math.pow(point1.y - point2.y, 2)));
    },

    random: function (min, max) {
        return Math.floor(Math.random() * (max - min + 1)) + min;
    }
};

let core_recover_time_k = 0; // 单位 one-loop
export const Planet = function (x, y, r) {

    this.x = x;
    this.y = y;
    this.r = r;

    this.colour = '#BF6D02';

    this.water_level = r;
    this.water_depth = r;
    this.snow_level = r;
    this.core_size = r / 3;
    this.atmosphere = 0;

    this.frozen = false;
};

Planet.prototype.addTree = function () {

    // 随机出一个角度
    let angle = Math.floor(Math.random() * 360);

    // 循环trees数组，判断每一个tree是否与所随机出来的角度之差在18度以外。如果没有重合，则生成新的树
    for (let i = 0; i < trees.length; i++) {

        let tree = trees[i];

        if (Utils.in_angle(angle, tree.angle, settings.tree_spread)) return;
    }

    trees.push(
        new Tree(angle));
};

Planet.prototype.update = function () {

    /*   by zxc 2020,12,29 TO recover planet  ⬇ */
    if (!this.frozen) core_recover_time_k = 0;
    /*   by zxc 2020,12,29 TO recover planet  ⬆ */

    if (this.core_size > 0) {
        // 水的吸收
        if (this.water_level > this.r) {
            this.water_level = Math.max(this.water_level - settings.soak_rate, this.r);
            this.water_depth = Math.max(this.water_depth - settings.soak_rate, 0);
        }

        // 水有渗入
        if (this.water_depth < this.r) {

            if (trees.length < custom_settings.maxTreesCount - 1) {
                if (trees.length < settings.max_trees && this.core_size && this.water_level <= this.r) {

                    // 大气层增加，到最大值才能生成云朵☁️。大气层现在暂时没有消失的方法。大气层会不断降雨。
                    if (this.atmosphere < settings.atmosphere_max) this.atmosphere += settings.atmosphere_growth;

                    if (Utils.chance(external_settings.treeChance)) {
                        this.addTree();
                    }
                }
            }

            this.water_depth += settings.dry_rate;
        }


        if (this.water_depth <= this.core_size) {
            // 水的深度淹没了核心，那么核心开始变小。天气开始变冷，直到水被全部吸收完，才开始转暖
            this.core_size -= settings.cool_rate;

        } else if (this.core_size < this.r / 3 && this.core_size > 0) {
            // 如果核心被淹没过，并且核心大小还存在，那么核心慢慢恢复。
            // 如果水一直淹没到核心消失，那么开始变天。下雪。
            this.core_size += settings.melt_rate;
        }


    } else {

        this.frozen = true;

        if (this.water_level > this.r) {
            this.water_level = Math.max(this.water_level - settings.soak_rate, this.r);
            this.water_depth = Math.max(this.water_depth - settings.soak_rate, 0);

            if (this.snow_level > this.r) {
                this.snow_level = Math.max(this.snow_level - settings.freeze_rate, this.r);
            }

        } else {

            /*   by zxc 2020,12,29 TO recover planet  ⬇ */
            if (core_recover_time_k < settings.core_recover_time) core_recover_time_k++;
            else this.core_size += settings.core_recover_rate;
            if (this.frozen && this.core_size > this.r / 3) this.frozen = false;
            /*   by zxc 2020,12,29 TO recover planet  ⬆ */

            if (this.snow_level <= this.water_level + settings.snow_height) this.snow_level += settings.freeze_rate;

            else if (this.snow_level > this.water_level + 4) this.snow_level -= settings.freeze_rate;
        }
    }

    if (this.atmosphere >= settings.atmosphere_max) {
        if (Utils.chance(external_settings.cloudChance)) {
            clouds.push(
                new Cloud(this));
        }
    }
};

Planet.prototype.draw = function () {

    // 1.大气层
    if (this.atmosphere > 0) {

        ctx.circle(
            this.x,
            this.y,
            this.r + settings.atmosphere_height,
            'rgba(70, 180, 240, ' + this.atmosphere.toFixed(2) + ')');
    }

    // 2.雪
    if (this.snow_level > this.r) {

        ctx.circle(
            this.x,
            this.y,
            this.snow_level,
            '#FFF');
    }

    // 3. 水平面的高度
    if (this.water_level > this.r) {

        ctx.circle(
            this.x,
            this.y,
            this.water_level,
            custom_settings.waterLevelColour);
    }

    // 4. 地壳核心 + 潮湿的土壤（水的下沉深度）
    if (this.water_depth < this.r) {

        // 4.1  潮湿的土壤
        ctx.circle(
            this.x,
            this.y,
            this.r,
            settings.wet_soil_colour);
        // 4.2  地壳核心
        ctx.circle(
            this.x,
            this.y,
            this.water_depth,
            this.colour);

    } else {
        // 4.2  地壳核心
        ctx.circle(
            this.x,
            this.y,
            this.r,
            this.colour);
    }

    // 核心变冷
    if (this.core_size < this.r / 3) {

        ctx.circle(
            this.x,
            this.y,
            this.r / 3,
            settings.cold_core_colour);
    }

    // 核心
    if (this.core_size > 0) {

        ctx.circle(
            this.x,
            this.y,
            this.core_size,
            settings.core_colour);
    }
};

//---------------------------------------------------------

export const Water = function (x, y, r, snow, gravity, colour, waterValue) {
    this.x = x;
    this.y = y;
    this.r = r;
    this.vx = 0;
    this.vy = 0;
    this.absorb = false;
    this.snow = snow || false;
    this.gravity = gravity || settings.gravity;
    this.water_colour = colour || 'rgba(40,160,220,0.4)';
    this.waterValue = waterValue || 0.15;
};

Water.prototype.update = function () {

    if (this.r < 1) {
        water.splice(water.indexOf(this), 1);
        return false;
    }

    if (this.absorb) {

        this.r -= 1;

        if (planet.water_level < planet.r + settings.atmosphere_height) {
            planet.water_level += this.waterValue;
        }

        return false;
    }

    let dist = Utils.distance(this, planet);
    let dist_x = planet.x - this.x;
    let dist_y = planet.y - this.y;

    let cos = dist_x / dist;
    let sin = dist_y / dist;

    let force = this.gravity * ((this.r * 2) * (planet.r * 2)) / Math.pow(dist, 2);

    this.vx += (cos * force);
    this.vy += (sin * force);

    if (dist < planet.r - this.r) {

        if (!this.snow) this.absorb = true;
        else this.r = 0;
    }

    this.x += this.vx;
    this.y += this.vy;

    return true;
};

Water.prototype.draw = function () {

    if (this.absorb) return;

    let colour = (this.snow) ? "#FFF" : this.water_colour;

    ctx.circle(this.x, this.y, this.r, colour);
};

//--------------------------------------------------------------------

let Tree = function (angle) {

    this.angle = angle;
    this.height = 0;
    this.max_height = settings.tree_height + Math.random() * 4;
    this.branches = [];
    this.width = 3;
    this.dying = 0;
    this.dead = false;
    this.dead_time = 0;
    this.dead_count = 0;
    this.colour = settings.branch_colours[Math.floor(Math.random() * settings.branch_colours.length)];
};

Tree.prototype.addBranch = function () {

    let angle = (this.branches.length * 3 > 3) ? -3 : this.branches.length * 3;
    let dist = (this.branches.length > 0) ? 1.5 : 1;

    this.branches.push(
        new Branch(
            angle,
            planet.r + (this.height / dist)));
};

Tree.prototype.update = function () {

    this.angle = Utils.alter_angle(this.angle, settings.spin_rate);

    if (this.dying > settings.drown_time) this.dead = true;

    /*   by zxc 2020,12,30 TO renew tree  ⬇ */
    if (this.dead) {
        if (planet.water_level >= planet.r + this.height) return;
        if (this.dead_time < 1000) {
            this.dead_time++;
            return;
        }
        for (let i = 0; i < this.branches.length; i++) {
            if (this.branches[i].r > 0) {
                return;
            }
        }
        this.dead_count++;
        this.dead = false;
        this.dying = 0;
        this.dead_time = 0;
        return;
    }
    /*   by zxc 2020,12,30 TO renew tree  ⬆ */

    // 1.没有被淹没,土壤有水分，树木慢慢死亡
    if (!planet.core_size || planet.water_depth >= planet.r) {
        this.dying += settings.tree_slow_growth_rate;
        return;
    }

    // 2.没有被淹没，土壤无水分,且水位线没有淹没树木,树木自然生长
    if (planet.water_level <= planet.r + this.height) {

        this.dying = 0;

        if (this.height < this.max_height) {
            this.height += settings.tree_growth_rate;
        }

        if (this.height >= this.max_height && this.branches.length < 3) {
            if (!this.branches.length || this.branches[this.branches.length - 1].grown) this.addBranch();
        }

    } else if (this.height > 2) {
        // 3.被淹没树木,且已经成熟
        this.dying += settings.tree_fast_growth_rate;
    }
};

Tree.prototype.draw = function () {

    ctx.shape(
        [
            Utils.circle_angle(
                planet.x,
                planet.y,
                planet.r,
                Utils.alter_angle(this.angle, -this.width)),
            Utils.circle_angle(
                planet.x,
                planet.y,
                planet.r + this.height,
                this.angle),
            Utils.circle_angle(
                planet.x,
                planet.y,
                planet.r,
                Utils.alter_angle(this.angle, this.width))],
        settings.tree_colour);

    let i = this.branches.length;
    while (i--) {
        this.branches[i].update(this);
        this.branches[i].draw(this);
    }
};

Tree.prototype.backgroundDraw = function () {

    let i = this.branches.length;
    while (i--) {
        this.branches[i].update(this);
        this.branches[i].backgroundDraw(this);
    }
};

//-----------------------------------------------------

let Branch = function (angle, dist) {
    this.r = 0;
    this.angle = angle;
    this.dist = dist;
    this.fruit = [];
    this.snow_height = 0;

    /* checks */
    this.snow = false;
    this.grown = false;
};

Branch.prototype.update = function (tree) {

    let coord = Utils.circle_angle(
        planet.x,
        planet.y,
        this.dist,
        Utils.alter_angle(tree.angle, this.angle));

    this.x = coord.x;
    this.y = coord.y;

    if (planet.core_size <= 0 && planet.water_level < planet.r + tree.height) {

        this.snow = true;

        if (this.snow_height < settings.snow_height) this.snow_height += settings.freeze_rate;

        let coord = Utils.circle_angle(
            planet.x,
            planet.y,
            this.dist + this.snow_height,
            Utils.alter_angle(tree.angle, this.angle));

        this.snowx = coord.x;
        this.snowy = coord.y;
    } else {

        if (this.snow_height > 0) {
            this.snow_height -= settings.freeze_rate;

            let coord = Utils.circle_angle(
                planet.x,
                planet.y,
                this.dist + this.snow_height,
                Utils.alter_angle(tree.angle, this.angle));

            this.snowx = coord.x;
            this.snowy = coord.y;
        } else this.snow = false
    }

    /*   by zxc 2020,12,30 TO renew tree  ⬇ */
    if (tree.dead) {
        if (this.fruit.length > 0) {
            for (let i = 0; i < this.fruit.length; i++) {
                this.fruit[i].on_tree = false;
                this.fruit[i].age = settings.fruit_rot + 1001;
            }
        }
        if (this.r >= 0) {
            this.r -= settings.branch_growth;
        }
        if (this.r < 0) this.r = 0;
        return;
    }

    if (this.r < settings.branch_size && planet.water_level < planet.r + tree.height) this.r += settings.branch_growth;
    else this.grown = true;
    /*   by zxc 2020,12,30 TO renew tree  ⬆ */

    if (this.grown && this.fruit.length < settings.max_fruit && planet.water_depth < planet.r) {

        if (Utils.chance(external_settings.fruitChance)) {
            this.fruit.push(
                new Fruit());
        }
    }
};

Branch.prototype.draw = function (tree) {

    if (this.snow) {

        ctx.circle(
            this.snowx,
            this.snowy,
            this.r,
            "#FFF");
    }

    ctx.circle(
        this.x,
        this.y,
        this.r, (tree.dead) ? settings.branch_dead_colour : tree.colour);

    let i = this.fruit.length;
    while (i--) {
        if (this.fruit[i].update(tree, this)) this.fruit[i].draw();
    }
};

Branch.prototype.backgroundDraw = function (tree) {


    let i = this.fruit.length;
    while (i--) {
        if (this.fruit[i].update(tree, this)) this.fruit[i].backgroundDraw();
    }
};

//---------------------------------------------------------------------

let Fruit = function () {

    this.r = 0;
    this.age = 0;
    this.on_tree = true;
    this.vx = 0;
    this.vy = 0;
    this.rotten = false;
    this.alpha = 1;
    this.colour = settings.fruit_colours[Math.floor(Math.random() * settings.fruit_colours.length)];
};

Fruit.prototype.update = function (tree, branch) {

    if (this.on_tree) {

        if (this.age > settings.fruit_age) {

            this.on_tree = false;
            return true;
        }

        this.x = branch.x;
        this.y = branch.y;

        if (this.r < settings.fruit_size && !tree.dead) this.r += settings.fruit_growth;
        else this.age++;

    } else {

        this.age++;

        if (this.age > settings.fruit_rot) {
            this.rotten = true;

            if (this.age > settings.fruit_rot + 1000) {

                this.alpha = Math.max(0, this.alpha - settings.fade_rate);

                if (this.alpha <= 0) {
                    branch.fruit.splice(branch.fruit.indexOf(this), 1);
                    return false;
                }
            }
        }

        let dist = Utils.distance(this, planet);
        let dist_x = -this.x + planet.x;
        let dist_y = -this.y + planet.y;
        let dist_real = dist - (this.r + planet.r);

        let cos = dist_x / dist;
        let sin = dist_y / dist;

        let force = settings.fruit_gravity * ((this.r * 2) * (planet.r * 2)) / Math.pow(dist, 2);

        if (dist < planet.water_level) {
            this.vx += (cos * force) / 3;
            this.vy += (sin * force) / 3;
        } else {
            this.vx += (cos * force);
            this.vy += (sin * force);
        }

        if (dist_real < 0) {
            this.x += (cos * (dist_real / 2));
            this.y += (sin * (dist_real / 2));
            this.vx += (cos * dist_real);
            this.vy += (sin * dist_real);

            if (dist > planet.water_level) {
                this.vx *= 0.8;
                this.vy *= 0.8;
            } else {
                this.vx *= 0.1;
                this.vy *= 0.1;
            }
        }

        let coord = Utils.circle_angle(
            planet.x,
            planet.y,
            dist,
            tree.angle + branch.angle);

        this.x = coord.x;
        this.y = coord.y;

        this.x += this.vx;
        this.y += this.vy;
    }

    return true;
};

Fruit.prototype.draw = function () {

    let colour = (this.rotten) ? settings.rotten_fruit_colour + this.alpha + ')' : this.colour;
    ctx.circle(this.x, this.y, this.r, colour);
};

Fruit.prototype.backgroundDraw = function () {
};

let Cloud = function () {

    this.angle = Math.floor(Math.random() * 360);
    this.parts = [];
    this.height = settings.atmosphere_height - (5 + Math.floor(Math.random() * 5));
    this.raining = false;
    this.rain = 0;
    this.dead = false;
    this.alpha = 0;
};

Cloud.prototype.update = function () {

    this.angle = Utils.alter_angle(this.angle, settings.spin_rate);

    if (planet.water_level > planet.r + settings.cloud_cutoff) this.dead = true;

    if (this.dead) {

        if (this.alpha <= 0) {
            clouds.splice(clouds.indexOf(this), 1);
            return false;
        }

        this.alpha = Math.max(0, this.alpha - settings.fade_rate);
        return true;

    } else {

        if (this.alpha < 0.6) {
            this.alpha = Math.min(1, this.alpha + settings.fade_rate);
        }
    }

    if (!this.parts.length) {

        for (let i = 0; i <= Utils.random(3, 10); i++) {
            this.parts.push({
                angle: this.angle + Utils.random(-3, 3),
                height: this.height + Utils.random(-2, 2),
                radius: Utils.random(3, 6)
            });
        }
    } else {

        if (Utils.chance(external_settings.rainingChance)) this.raining = true;

        if (this.raining && Utils.chance(external_settings.rainRate)) {
            let part = this.parts[Utils.random(0, this.parts.length - 1)];
            water.push(
                new Water(
                    part.x + Utils.random(-part.radius * 0.5, part.radius * 0.5),
                    part.y + Utils.random(-part.radius * 0.5, part.radius * 0.5),
                    1.5,
                    planet.frozen,
                    settings.gravity * 5));
            this.rain++;
        }

        if (this.rain > external_settings.rainTime && Utils.chance(external_settings.cloudDeadChance)) {
            this.dead = true;
        }
    }

    return true;
};

Cloud.prototype.draw = function () {

    let i = this.parts.length;
    while (i--) {

        let part = this.parts[i];

        let coord = Utils.circle_angle(
            planet.x,
            planet.y,
            planet.r + part.height,
            this.angle + part.angle);

        part.x = coord.x;
        part.y = coord.y;

        ctx.circle(
            coord.x,
            coord.y,
            part.radius,
            'rgba(255, 255, 255,' + this.alpha + ')');

    }
};

